#include "NTest.h"
#include <vector>
#include <map>
#include <string>
#include <iostream>
#include <algorithm>
#include <ctype.h>
#include <math.h>


/*----------------------------------------------------------
NTestUnitApp
-----------------------------------------------------------*/
class NTestUnitApp : public NTestUnitBase
{
public:
	NTestUnitApp(const char * InName, int InIdx)
		:mName(InName)
		,mIndex(InIdx)
	{}

	virtual void run() {}
	virtual const char * name() const {
		return mName.c_str();
	}
	virtual int index() const {
		return mIndex;
	}
private:
	std::string mName;
	int			mIndex;
};


NTestUnitBase * NCreateTestUnitApp( const char * InName, int InIdx )
{
	return new NTestUnitApp(InName, InIdx);
}

void NDeleteTestUnitApp( NTestUnitBase * InUnit )
{
	delete InUnit;
}
/*----------------------------------------------------------
NTestFuncsApp
-----------------------------------------------------------*/
class NTestFuncsApp : public NTestFuncsBase
{
public:
	NTestFuncsApp(const char * InName):mName(InName) {}

	void run() {}
	const char * name() const { return mName.c_str(); }
	
private:
	std::string				mName;
};

NTestFuncsBase * NCreateTestFuncsApp( const char * InName )
{
	return new NTestFuncsApp(InName);
}

void NDeleteTestFuncsApp( NTestFuncsBase * InFuncs )
{
	delete InFuncs;
}

/*----------------------------------------------------------
NTestCaseApp
-----------------------------------------------------------*/
class NTestCaseApp : public NTestCaseBase
{
public:
	virtual void addFuncs(NTestFuncsBase * InFuncs);
	virtual const char * name() const;
	virtual void setName(const char * InName) { mName = InName;	}
	virtual void run();
	virtual void setup() { NCHECK(0); }
	virtual void close() { NCHECK(0); }
	virtual bool pause() { NCHECK(0); return false; }
private:
	std::string							mName;
	std::vector<NTestFuncsBase *>		mFuncs;
};

void NTestCaseApp::addFuncs( NTestFuncsBase * InFuncs )
{
	mFuncs.push_back(InFuncs);
}

const char * NTestCaseApp::name() const
{
	return mName.c_str();
}

void NTestCaseApp::run()
{
	std::cout << name() << std::endl;

	for (unsigned int ui = 0; ui < mFuncs.size(); ++ui)
	{
		NTestFuncsBase * Funcs = mFuncs[ui];
		Funcs->run();
		std::cout << "\t" << Funcs->name() << " ...Done!" << std::endl;
	}

	std::cout << "Done!" << std::endl;
}

NTestCaseBase * NCreateTestCaseApp()
{
	return new NTestCaseApp;
}

void NDeleteTestCaseApp( NTestCaseBase * InCase )
{
	delete InCase;
}
/*----------------------------------------------------------
NTestHostApp
-----------------------------------------------------------*/
class NTestHostApp : public NTestHostBase
{
	struct NCaseId
	{
		bool operator ==(const NCaseId & In) const
		{
			return Index == In.Index;
		}

		bool operator <(const NCaseId & In) const
		{
			return Index < In.Index;
		}
		
		int						Index;
		NTestCaseBase *			Case;
	};

	typedef std::multimap<std::string, NTestCaseBase *>		CasesMapType;

	virtual int addCase( NTestCaseBase * InCase, const char * InName );
	virtual int addCase( NTestCaseBase * InCase, const char * InName, int InId );
	virtual int addUnit(NTestUnitBase * InUnit);
	virtual void run(const char * InCmd);

	void runCase(NTestCaseBase * InCase);
	void runUnit(NTestUnitBase * InUnit);

	CasesMapType			mCases;
	std::vector<NCaseId>	mCaseIds;
	std::vector<NTestUnitBase *>		mUnits;
};

int NTestHostApp::addCase( NTestCaseBase * InCase, const char * InName )
{
	InCase->setName(InName);

	mCases.insert(std::make_pair(InCase->name(), InCase));

	return 0;
}

int NTestHostApp::addCase( NTestCaseBase * InCase, const char * InName, int InId )
{
	InCase->setName(InName);

	//for (unsigned int ui = 0; ui < mCaseIds.size(); ++ui)
	//{
	//	const NCaseId & CaseId = mCaseIds[ui];

	//	if (CaseId.Index == InId)
	//	{
	//		std::cout << "Failed to register test case \"" << InCase->name() << "\"" << std::endl;
	//		std::cout << "\tId " << CaseId.Index << " is used by test case " << CaseId.Case->name() << std::endl;
	//		return -1;
	//	}
	//}

	NCaseId CaseId;
	CaseId.Index	= InId;
	CaseId.Case		= InCase;
	mCaseIds.push_back(CaseId);
	return 0;
}

bool UnitSortIndex(NTestUnitBase * InA, NTestUnitBase * InB)
{
	return InA->index() < InB->index();
}

bool UnitSortName(NTestUnitBase * InA, NTestUnitBase * InB)
{
	if (InA->index() != InB->index())
	{
		return false;
	}

	std::string a(InA->name());
	std::string b(InB->name());
	return a < b;
}

void NTestHostApp::run( const char * InCmd )
{
	std::sort(mUnits.begin(), mUnits.end(), UnitSortIndex);
	std::sort(mUnits.begin(), mUnits.end(), UnitSortName);

	for (unsigned int ui = 0; ui < mUnits.size(); ++ui)
	{
		NTestUnitBase * unit = mUnits[ui];
		NCHECK(unit);
		runUnit(unit);
	}

	std::sort(mCaseIds.begin(), mCaseIds.end());
	for (unsigned int ui = 0; ui < mCaseIds.size(); ++ui)
	{
		NTestCaseBase * Case = mCaseIds[ui].Case;
		NCHECK(Case);
		runCase(Case);
	}

	for (CasesMapType::iterator It = mCases.begin();
		It != mCases.end();
		++It)
	{
		NTestCaseBase * Case = It->second;
		NCHECK(Case);
		runCase(Case);
	}
	
	std::cout << "Congratulations, all tests have past!" << std::endl;
	std::cout << "Press any key to continue" << std::endl;
	NPAUSE();
}

void NTestHostApp::runCase( NTestCaseBase * InCase )
{
	InCase->setup();
	InCase->run();
	InCase->close();

	if (InCase->pause())
	{
		std::cout << "Test case have done, will you press any key to continue?" << std::endl;
		NPAUSE();
	}
}

int NTestHostApp::addUnit( NTestUnitBase * InUnit )
{
	mUnits.push_back(InUnit);
	return 0;
}

void NTestHostApp::runUnit( NTestUnitBase * InUnit )
{
	InUnit->run();
	std::cout << InUnit->name() << " ...Done!" << std::endl;
}

NTestHostBase * NCreateTestHostApp()
{
	return new NTestHostApp;
}

void NDeleteTestHostApp( NTestHostBase * InHost )
{
	delete InHost;
}

/*----------------------------------------------------------
Miscellaneous
-----------------------------------------------------------*/
bool NStrEqual( const char * InA, const char * InB )
{
	std::string StrA = InA;
	std::string StrB = InB;
	return StrA == StrB;
}

bool NStrEqual( const wchar_t * InA, const wchar_t * InB )
{
	std::wstring StrA = InA;
	std::wstring StrB = InB;
	return StrA == StrB;
}

static void StrToUpper(std::string & Out)
{
	for (unsigned int ui = 0; ui < Out.length(); ++ui)
	{
		Out[ui] = toupper(Out[ui]);
	}
}

static void StrToUpper(std::wstring & Out)
{
	for (unsigned int ui = 0; ui < Out.length(); ++ui)
	{
		Out[ui] = towupper(Out[ui]);
	}
}

bool NStrSame( const char * InA, const char * InB )
{
	std::string StrA = InA;
	std::string StrB = InB;
	StrToUpper(StrA);
	StrToUpper(StrB);
	return StrA == StrB;
}

bool NStrSame( const wchar_t * InA, const wchar_t * InB )
{
	std::wstring StrA = InA;
	std::wstring StrB = InB;
	StrToUpper(StrA);
	StrToUpper(StrB);
	return StrA == StrB;	
}

void NPause()
{
	system("pause");
}

bool NFloatEqual( double InValue, double InEq )
{
	double sub = InValue - InEq;
	return fabs(sub) < 0.000001;
}

